<?php
/**
 * DMFramework
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @url http://dmframework.nl
 */

/**
 * The parser can be used to "parse" pthml view and layout files.
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @package System.Library
 */
class Dmf_Parser
{    
    /**
     * Filename
     * 
     * @var string 
     */
    private $_filename;
    
    /**
     * Temp file name
     * 
     * @var string
     */
    private $_tmpFile;
    
    /**
     * Last known file name
     * 
     * @var string
     */
    private static $_lastKnownFilename;
    
    /**
     * All replaces (after rendering)
     */
    private $_afterReplaces = array(
        'from' => array( 
            '/<\?[^p]/',
            '/\@([^;\n]*);/',
            '/\@([^" <\n]*)/',
            '/<dmf\:([^ ]*) ([^>]*)><\/([^>]*)>/',
            '/<dmf\:([^ ]*)><\/([^>]*)>/',
            "/\}\n\?>\n<\?php/"
        ),
        'to'   => array(
            '<?php ',
            '<?php echo $1; ?>',
            '<?php echo $1; ?>',
            '<?php $1Widget::parse( \'$1\', \'$2\', $this ); ?>',
            '<?php $1Widget::parse( \'$1\', null, $this ); ?>',
            '}'
        )
    );
    
    /**
     * All replaces (before rendering)
     */
    private $_beforeReplaces = array(
        'from' => array(
            '/\@\@/',
            '/\@url/',
            '/\@include\("([^"]*)"\)/'
        ),
        'to'   => array(
            '&#64;',
            '@$this->url',
            '<?php $this->renderFile(\'$1\'); ?>'
        )
    );
    
    /**
     * All blocks
     * 
     * @var array
     */
    private $_blocks = array(
            'foreach'   => '\@foreach',
            'if'        => '\@if',
            'while'     => '\@while',
            'for'       => '\@for',
            'else'      => '\@else'
        );
    
    /**
     * Constructor
     * 
     * @param string $filename
     * @throws Dmf_Exception_FileNotFound 
     */
    public function __construct( $filename )
    {
        if ( ! file_exists( $filename ) )
        {
            throw new Dmf_Exception_FileNotFound( 'File: "' . $filename . '" not found.' );
        }
        
        $this->_filename = $filename;
        self::$_lastKnownFilename = $filename;
    }
    
    /**
     * Get the last known filename
     * 
     * @return string
     */
    public static function getLastKnownFilename()
    {
        return self::$_lastKnownFilename;
    }
    
    /**
     * Parse the file
     * 
     * @param string $content
     */
    public function parseContent( $content )
    {
        //Prepare content
        $content = preg_replace( $this->_beforeReplaces['from'], $this->_beforeReplaces['to'], $content );
        
        //Initialize variables
        $body = '';
        $lastBlock = null;
        $isParsing = false;
        $start = 0;
        $lines = explode( "\n", $content );
        
        //Loop through lines
        foreach( $lines as $line )
        {
            $block = null;
            
            //If parser-tag is found, block is null when not found
            if ( strstr( $line, '@' ) )
            {
                foreach( $this->_blocks as $block => $regex )
                {
                    if ( preg_match( '/' . $regex . '/', $line ) )
                    {
                        break;
                    }

                    $block = null;
                }
            }
            
            //If a valid block is found
            if ( $block !== null )
            {
                $isParsing = true;
                
                if ( strstr( $line, '@' . $block ) )
                {
                    $lastBlock = $block;
                    
                    preg_match( '/\@' . $block . '(.*)/', $line, $match );
                    
                    $body .= '<?php ' . "\n" . $block . ' ' . $match['1'];
                }
            }
            elseif( $isParsing && strstr( $line, '{' ) )
            {
                $start++;
                
                $body .= "{\n" . '?>';
            }
            elseif( $isParsing && strstr( $line, '}' ) )
            {
                $start--;
                
                $body .= "<?php \n}\n" . '?>';
            }
            else
            {
                if ( $start == 0 )
                {
                    $isParsing = false;
                }
                
                $body .= $line;
            }
            
            $body .= "\n";
        }
        
        //Replace the body after parsing
        $body = preg_replace( $this->_afterReplaces['from'], $this->_afterReplaces['to'], $body );
        
        return $body;
    }
    
    /**
     * Get the parsed tmp-file name
     * 
     * @return string
     */
    public function getFile()
    {
        $this->_tmpFile = tempnam( 'system/cache', 'Dmf_Resource' );
        
        file_put_contents( $this->_tmpFile, $this->parseContent( file_get_contents( $this->_filename ) ), LOCK_EX );
        
        return $this->_tmpFile;
    }
    
    /**
     * Remove the tmp-file 
     */
    public function removeTempFile()
    {
        if ( file_exists( $this->_tmpFile ) && is_writable( $this->_tmpFile ) )
        {
            @unlink( $this->_tmpFile );
        }
    }
}